class BException(Exception):  #继承Exception基类
    pass

class CException(BException):  #继承BException基类
    pass

class DException(CException):  #继承CException基类
    pass
#
# for cls in [BException, CException, DException]:
#     try:
#         raise cls()  #抛出异常
#     except DException:
#         print("D")
#     except CException:
#         print("C")
#     except BException:
#         print("B")
'''
请注意如果 except 子句被颠倒（把 except BException 放到第一个），
它将打印 B，B，B — 因为DException类继承CException类，CException类继承BException类，
将 except BException 放到第一个可以匹配这三个异常，后面的 except 就不会执行
'''
# 子句可以省略异常名，以用作通配符，捕获所有异常
# try:
#     raise BException()  #抛出异常
# except DException:
#     print("D")
# except:
#     print("处理全部其它异常") #处理全部其它异常

'''
一个 try 语句可能有多个 except 子句，以指定不同异常的处理程序，
最多会执行一个处理程序。 处理程序只处理相应的 try 子句中发生的异常，
而不处理同一 try 语句内其他处理程序中的异常。一个 except 子句可以将多个异常命名为带括号的元组。
'''
# try:
#     raise BException()  #抛出异常
# except (BException, DException):
#     print("D")
# except:
#     print("处理全部其它异常") #处理全部其它异常
# else:
#     print("没有异常发生") #没有异常发生

# fibally
'''
这里注意 finally 和 else 的区别，finally 是无论是否有异常都会执行，
而 else 语句只有没有异常时才会执行。也就是说如果没有异常，那么 finally 和 else 都会执行
'''
# try:
#     raise BException()  #抛出异常
# except (BException, DException):
#     print("D")
# except:
#     print("处理全部其它异常") #处理全部其它异常
# else:
#     print("没有异常发生") #没有异常发生
# finally:
#     print("你们绕不过我，必须执行") #必须执行的代码

'''
except 子句可以在异常名称后面指定一个变量。
这个变量和一个异常实例绑定，它的参数是一个元组，通常包含错误字符串，
错误数字，错误位置，存储在 .args 中。为了方便起见，异常实例定义了__str__() ，因此可以直接打印参数而无需引用 .args
'''
# try:
#     x = 1 / 0  # 除数为0
# except ZeroDivisionError as err: #为异常指定变量err
#     print("Exception")
#     print(err.args) #打印异常的参数元组
#     print(err) #打印参数，因为定义了__str__()

# 触发异常 raise语句
def diyException(level):
    if level > 0:
        raise Exception("raise exception", level)  #主动抛出一个异常，并且带有参数
        print('我是不会执行的') #这行代码不会执行

# try:
#    diyException(2)  #执行异常方法
# except Exception as err: #捕获异常
#     print(err) #打印异常参数
# 捕获上面代码抛出的异常
# try:
#    diyException(2)  #执行异常方法
# except 'error level' as err: #捕获异常
#     print(err) #打印异常参数

# import traceback
#
# try:
#     diyException(2)#执行异常
# except Exception: # 捕获异常
#     traceback.print_exc()

# TODO 自定义异常
# class DiyError(RuntimeError):
#     def __init__(self, arg):
#         self.args = arg
#
# try:
#     raise DiyError("my diy exception") #触发异常
# except DiyError as e:
#     print(e)

# TODO 关键词 with 语句就可以保证诸如文件之类的对象在使用完之后一定会正确的执行他的清理方法
with open("myfile.txt") as f:
    for line in f:
        print(line, end="")